package ru.niir.goodfil.utils.validation;

import org.apache.log4j.Logger;

import java.util.ArrayList;

/**
 * Структура хранит результат проверки на удаление сущности.
 * После выполнеения проверки на возможность удаления формируется такая структура.
 * Она содержит, во-первых, результат самой проверки (можно удалять или нет), и, во-вторых
 * перечень сущностей, связанных с данной (для случая, если удалять нельзя конечно).
 */
public class RemoveCheckResult
{
    /** Логгер */
    private static final Logger logger = Logger.getLogger(RemoveCheckResult.class);

    /**
     * Результат провери: либо удаление возможно и связанные сущности отсутствуют,
     * либо удаление не возможно, т.к. связанные сущности существуют.
     */
    private final boolean result;

    /**
     * Наименование класса, для которого производится проверка
     */
    private final String className;

    /**
     * Список связаных сущностей, формируется если результат проверки отрицательный
     */
    private final ArrayList<String> linkedObjects = new ArrayList<String>();

    /**
     * Конструктор
     * @param result результат провери: либо удаление возможно и связанные сущности отсутствуют, либо удаление не возможно, т.к. связанные сущности существуют.
     * @param clas наименование класса, для которого производится проверка
     */
    private RemoveCheckResult(boolean result, Class clas)
    {
        this.result = result;
        this.className = clas.getSimpleName();
    }

    /**
     * Статический конструктор для успешной проверки
     * @param clas класс, для которого производится проверка
     * @return RemoveCheckResult
     */
    public static RemoveCheckResult Ok(Class clas)
    {
        return new RemoveCheckResult(true, clas);
    }

    /**
     * Статический конструктор для неудачной проверки
     * @param clas класс, для которого производится проверка
     * @return RemoveCheckResult
     */
    public static RemoveCheckResult Error(Class clas)
    {
        logger.warn("Remove check failed for class: " + clas.getName());

        return new RemoveCheckResult(false, clas);
    }

    /**
     * Статический конструктор для неудачной проверки
     * @param clas класс, для которого производится проверка
     * @param linkedClass класс связанной сущности
     * @return RemoveCheckResult
     */
    public static RemoveCheckResult Error(Class clas, Class linkedClass)
    {
        logger.warn("Remove check failed for class: " + clas.getName() + " (" + linkedClass.getName() + ")");

        RemoveCheckResult rcr = new RemoveCheckResult(false, clas);
        rcr.addLinkedObject(linkedClass);
        return rcr;
    }

    /**
     * Статический конструктор для неудачной проверки
     * @param clas класс, для которого производится проверка
     * @param linkedClasses список классов связанных сущностей
     * @return RemoveCheckResult
     */
    public static RemoveCheckResult Error(Class clas, Class[] linkedClasses)
    {
        logger.warn("Remove check failed for class: " + clas.getName() + " " + joinAsString(linkedClasses));
        
        RemoveCheckResult rcr = new RemoveCheckResult(false, clas);
        for (Class linkedClass : linkedClasses) rcr.addLinkedObject(linkedClass);
        return rcr;
    }

    private static String joinAsString(Class[] linkedClasses)
    {
        StringBuilder sb = new StringBuilder("(");
        for (int i = 0; i < linkedClasses.length - 1; i++) sb.append(linkedClasses[i].getSimpleName() + ", ");
        sb.append(linkedClasses[linkedClasses.length - 1].getSimpleName() + ")");
        return sb.toString();
    }

    /**
     * Получить в виде строки перечень связанных объектов.
     * Строка формируется в виде: (Class1, Class2, Class3)
     * @return перечень связанных объектов в виде строки 
     */
    public String getLinkedObjectsAsString()
    {
        StringBuilder sb = new StringBuilder("(");
        for (int i = 0; i < linkedObjects.size() - 1; i++) sb.append(linkedObjects.get(i) + ", ");
        sb.append(linkedObjects.get(linkedObjects.size() - 1) + ")");
        return sb.toString();
    }

    /**
     * Добавить класс связанной сущности
     * @param clas класс связанной сущности
     */
    public void addLinkedObject(Class clas)
    {
        assert !result;

        linkedObjects.add(clas.getSimpleName());
    }

    /**
     * Получить список наименований классов связанных сущностей
     * @return список наименований классов связанных сущностей
     */
    public String[] getLinkedObjects()
    {
        return (String[]) linkedObjects.toArray();
    }

    /**
     * Возвращает наименование класса, для которого производится проверка
     */
    public String getClassName()
    {
        return className;
    }

    /**
     * Возвращает true, если удаление возможно
     */
    public boolean isOk()
    {
        return result;
    }

    /**
     * Возвращает true, если удаление не возможно, т.к. есть связанные сущности
     */
    public boolean isError()
    {
        return !result;
    }
}
